package main

import (
	"bufio"
	"errors"
	"fmt"
	"net"
	"strconv"
	"strings"
)

var toSlaveCommand map[string]bool

var (
	redisMasterConn string = "127.0.0.1:6379"
	redisSlaveConn string = "127.0.0.1:6380"
)


func main() {

	l,err := net.Listen("tcp","0.0.0.0:8888")
	if err != nil {
		panic(err)
	}

	fmt.Println("服务器已经启动好了，监听地址: 0.0.0.0:8888")

	// 维护从库执行的命令
	toSlaveCommand = make(map[string]bool,0)

	// key
	toSlaveCommand["GET"] = true

	// list
	toSlaveCommand["LLEN"] = true
	toSlaveCommand["LRANGE"] = true

	// hash
	toSlaveCommand["HEXISTS"] = true
	toSlaveCommand["HGET"] = true
	toSlaveCommand["HLEN"] = true
	toSlaveCommand["HLEN"] = true

	// set


	for {
		conn ,err := l.Accept()
		if err != nil {
			fmt.Println("accept error " , err)
			continue
		}

		fmt.Println(conn.RemoteAddr()," 连接到Redis中转服务器")

		// 为连接上来的每个conn分配主从redis conn
		masterConn , err := net.Dial("tcp",redisMasterConn);if err != nil {
			fmt.Println("dial redis master conn error: " , err)
			continue
		}

		slaveConn, err := net.Dial("tcp",redisSlaveConn);if err != nil {
			fmt.Println("dial redis slave conn error: " , err)
			continue
		}

		go worker(conn,masterConn,slaveConn)
	}
}

// worker
func worker(conn net.Conn,masterRedisConn net.Conn,slaveRedisConn net.Conn) {
	defer func() {
		fmt.Println(conn.RemoteAddr()," 断开连接")
		conn.Close()
		masterRedisConn.Close()
		slaveRedisConn.Close()
	}()
	reader := bufio.NewReader(conn)

	for {
		var ss []interface{}
		if toArgs(reader,&ss) != nil {
			conn.Write([]byte(string("-client send msg error")))
			return
		}

		com := genArrayArgs(ss)

		switch strings.ToTitle(ss[0].(string)) {
		case "COMMAND":
			conn.Write([]byte(string("+OK\r\n")))
		case "AUTH":
			fmt.Println(conn.RemoteAddr(),"获取命令: " ,ss,"转发到主库")
			toAgent(com,conn,masterRedisConn,true)
			fmt.Println(conn.RemoteAddr(),"获取命令: " ,ss,"转发到从库")
			toAgent(com,conn,slaveRedisConn,false)
		default:
			_ ,ok := toSlaveCommand[strings.ToTitle(ss[0].(string))]; if ok {
				fmt.Println(conn.RemoteAddr(),"获取命令: " ,ss,"转发到从库")
				toAgent(com,conn,slaveRedisConn,true)
			} else {
				fmt.Println(conn.RemoteAddr(),"获取命令: " ,ss,"转发到主库")
				toAgent(com,conn,masterRedisConn,true)
			}
		}
	}
}

// 转发信息到真实的服务器
func toAgent(com []byte,conn net.Conn,redisConn net.Conn,flag bool) {
	redisConn.Write(com)
	buf := make([]byte,10240000)
	n , err := redisConn.Read(buf)
	if err != nil {
		fmt.Println(err)
	}
	if flag {
		conn.Write(buf[:n])
	}
}

// 将[]interface 类型转换为RESP数据
func genArrayArgs(ss []interface{}) (respMsg []byte) {
	bodyLen := len(ss)

	respMsg = append(respMsg, []byte(string("*"))...)
	respMsg = append(respMsg, []byte(strconv.Itoa((bodyLen)) + "\r\n")...)

	for i:=0;i<len(ss);i++ {
		switch ss[i].(type) {
		case int:
			respMsg = append(respMsg, []byte(string(":"))...)
			x := ss[i].(int)
			respMsg = append(respMsg, []byte(strconv.Itoa((x)) + "\r\n")...)
		case string:
			respMsg = append(respMsg, []byte(string("$"))...)
			str1 := ss[i].(string)
			respMsg = append(respMsg, []byte(string(strconv.Itoa((len(str1))) + "\r\n"))...)
			respMsg = append(respMsg, []byte(string(str1+"\r\n"))...)
		}
	}
	return
}

// 将RESP转换为[]interface 类型
func toArgs(reader *bufio.Reader,ss *[]interface{}) (error) {
	line , _ , _ := reader.ReadLine()

	if len(line) < 2 {
		return errors.New("获取报文有误")
	}

	switch line[0] {
	case '*':
		n , err := byteToInt(line[1:]);if err != nil {
			return errors.New("转为数字错误")
		}
		// 递归
		for i:=0;i<n;i++ {
			toArgs(reader,ss)
		}
	case '$':
		n , err := byteToInt(line[1:]);if err != nil {
			return errors.New("转为数字错误")
		}
		buf := make([]byte,n)
		_,err = reader.Read(buf) ; if err != nil {
			return errors.New("获取命令失败")
		}

		*ss = append(*ss, string(buf))
		crlf := make([]byte,2)
		reader.Read(crlf)
		if string(crlf) != "\r\n" {
			return errors.New("获取命令失败")
		}
	}
	return nil
}

// []byte 转为 int
// 例如 []byte(string("11")) 转为 int类型的 11
func byteToInt(number []byte) (int , error) {
	var sum int
	x := 1
	for i:=len(number)-1;i>=0;i-- {
		if number[i] < '0' || number[i] > '9' {
			return 0 , errors.New("toNumberError")
		}
		sum += int(number[i] - '0') * x
		x = x * 10
	}

	return sum,nil
}

